package cn.yyx.aclservice.security.filter;
import cn.yyx.aclservice.entity.User;
import cn.yyx.aclservice.result.JwtUtils;
import cn.yyx.common.R;
import cn.yyx.aclservice.result.ResponseUtil;
import cn.yyx.aclservice.security.entity.SecurityUser;
import cn.yyx.aclservice.security.security.TokenManager;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
/**
 * <p>
 * 登录过滤器，继承UsernamePasswordAuthenticationFilter，对用户名密码进行登录校验
 * </p>
 * @author qy
 * @since 2019-11-08
 */
public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter {
    private AuthenticationManager authenticationManager;
    private TokenManager tokenManager;
    private RedisTemplate redisTemplate;
    /**
     * 构造方法
     * @param authenticationManager
     * @param tokenManager
     * @param redisTemplate
     */
    public TokenLoginFilter(AuthenticationManager authenticationManager, TokenManager tokenManager, RedisTemplate redisTemplate) {
        this.authenticationManager = authenticationManager;
        this.tokenManager = tokenManager;
        this.redisTemplate = redisTemplate;
        this.setPostOnly(false);
        //设置登陆路径等价于之前 在WebSecurityConfig中定义的 http.formLogin().loginProcessingUrl("/login") //登录表单提交的地址
        this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/admin/acl/login", "POST"));
    }
    //尝试认证 覆写了父类UsernamePasswordAuthenticationFilter的 attemptAuthentication方法
    @Override
    public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res)
            throws AuthenticationException {
        try {
            //根据用户名查询用户信息
            User user = new ObjectMapper().readValue(req.getInputStream(), User.class);
            // 认证管理器的 authenticate(Authentication authentication)
            // 需要Authentication接口的子类类型（这里传递的就是UsernamePasswordAuthenticationToken）
            // 回忆源码： 难点：return this.getAuthenticationManager().authenticate(authRequest);
            return authenticationManager.authenticate(
                    //回忆源码：return this.createSuccessAuthentication(principalToReturn, authentication, user);
                    //回忆源码： protected Authentication createSuccessAuthentication(Object principal, Authentication authentication, UserDetails user)
                    // { UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(principal,
                    //  authentication.getCredentials(),
                    //  this.authoritiesMapper.mapAuthorities(user.getAuthorities()));
                    //第一个参数是 身份信息  第二个是认证信息  第三个是用来保存权限的集合
                    new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword(), new ArrayList<>()));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    /**
     * 登录成功
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest req, HttpServletResponse res, FilterChain chain,
                                            Authentication auth) throws IOException, ServletException {
        SecurityUser user = (SecurityUser) auth.getPrincipal();
        String  mid= user.getCurrentUserInfo().getId();
        String username=user.getCurrentUserInfo().getUsername();
        String token = JwtUtils.getJwtTokenForAdmin(mid,username);
        //把权限放到redis中
        redisTemplate.opsForValue().set(user.getCurrentUserInfo().getUsername(), user.getPermissionValueList());
        //添加到浏览器cookie
        ResponseUtil.out(res, R.ok().data("token", token));
    }
    /**
     * 登录失败
     * @param request
     * @param response
     * @param e
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
                                              AuthenticationException e) throws IOException, ServletException {
        System.out.println("认证失败了！");
        e.printStackTrace();
        ResponseUtil.out(response, R.error());
    }
}
